<?php

if( !defined('PHP_VERSION_ID') )
{
	$_v = explode('.', PHP_VERSION);
	define('PHP_VERSION_ID', ($_v[0] * 10000 + $_v[1] * 100 + $_v[2]));
}
if( PHP_VERSION_ID < 50200 )
{
	die(basename(__FILE__)." needs at least PHP 5.2 to work.");
}

if( !function_exists("_") )
{

	function _($d="")
	{
		$A = is_array($d);
		$S = isset($_SERVER['HTTP_USER_AGENT']);
		echo($A && $S ? '<pre>' : '').print_r($d, true).($A && $S ? '</pre>' : '').($S ? "<br>\n" : "\n");
	}

}

if( !function_exists("struct") )
{

	function struct($array=false, $key=false, $return=false)
	{
		return struct::draw($array, $key, $return);
	}

}

/**
 * Struct is a class to outsource and simplify the creation of arrays.
 * The syntax used to create structured data is similar to php except some unneeded markups.
 * Syntax example for a stuct key within php (string)
 * struct::set("layer1->layer2->layer3=A String");
 * 
 * Syntax example for a stuct key within an external file
 * SET layer1->layer2->layer3=A String
 * @author echterhoff
 * @license LGPL see license.txt
 *
 */
class struct
{

	public static $sesh;
	public static $creator;
	public static $defaultcreator;
	public static $order;
	private static $store = array();
	public static $meta = array();
	private static $assignoperator = "[:=]";
	private static $accesskey = array();
	private static $keyaccess = array();
	private static $lastkey;
	private static $response_debug;
	private static $group;
	private static $each = array();

	/**
	 * Command to control the target group for a struct modification
	 * @param mixed $key
	 */
	public static function group($key=false)
	{
		if( $key )
		{
			self::$group = $key."->";
		}
		else
		{
			unset(self::$each[self::$group]);
			self::$group = "";
		}
	}

	/**
	 * Define any meta data to a key
	 * @param string $key
	 * @param string $metaname
	 * @param string $val
	 */
	public static function set_meta($key, $metaname=null, $val=null)
	{
		return self::def($key, $metaname, $val);
	}

	/**
	 * Short alias for set_meta
	 * @param string $key
	 * @param string $metaname
	 * @param string $val
	 */
	public static function meta($key, $metaname=null, $val=null)
	{
		return self::def($key, $metaname, $val);
	}

	/**
	 * Short alias for set_meta
	 * @param string $key
	 * @param string $metaname
	 * @param string $val
	 */
	public static function def($key, $metaname=null, $val=null)
	{
		$autotrue = false;
		if( !isset($metaname) && !isset($val) )
		{
			$metaname = $key;
			$autotrue = true;
		}
		if( isset($val) )
		{
			self::$lastkey = $key;
		}
		if( !isset($val) )
		{
			$val = $metaname;
			$metaname = $key;
		}
		if( $autotrue )
		{
			$val = true;
		}
		//echo $metaname."\n";
		self::set_array(self::$meta, self::$group.self::$lastkey."->".$metaname, $val);
		return true;
	}

	/**
	 * Belongs to the response method and tries to record all events to self::$response_debug
	 * @param boolean $setto
	 */
	public static function response_debug($setto=true)
	{
		ob_start();
		self::$response_debug = $setto;
	}

	/**
	 * Returns the desired element
	 * @param string $trigger
	 * @param string $key
	 * @param string $val
	 * @return array
	 */
	public static function response($trigger="ajax", $key="key", $val="val")
	{
		if( isset($_REQUEST[$trigger]) )
		{
			if( isset(self::$keyaccess[$_REQUEST[$key]]) )
			{
				$ret = array("key" => self::$keyaccess[$_REQUEST[$key]], "val" => $_REQUEST[$val]);
			}
			elseif( isset(self::$accesskey[$_REQUEST[$key]]) )
			{
				$ret = array("key" => $_REQUEST[$key], "val" => $_REQUEST[$val]);
			}
			if( self::$response_debug )
			{
				ob_end_clean();
				echo json_encode($ret);
				exit;
			}
			return $ret;
			exit;
		}
		return false;
	}

	/**
	 * Helper to access static method to retrieve meta data
	 * @param string $name
	 * @param mixed $args
	 * @return mixed
	 */
	public static function __callStatic($name, $args)
	{
		$meta = self::get($args[0], self::$meta);
		return (!isset($meta[$name]) ? null : $meta[$name]);
	}

	/**
	 * Sets a value to the corresponding key
	 * @param string $key
	 * @param mixed $value
	 * @param array $array Target array in PHP
	 * @return boolean
	 */
	public static function set($key, $value, &$array=false)
	{
		self::$lastkey = $key;
		if( $array === false )
		{
			$array = & self::$store;
		}
		self::set_array($array, self::$group.$key, $value);
		self::$accesskey[$key] = md5($key);
		self::$keyaccess[md5($key)] = $key;
		if( preg_match("/passw/i", $key) ) self::def($key, "ispassword", true);
		self::def($key, "default", $value);
		if( isset(self::$each[self::$group]) )
		{
			self::def(self::$each[self::$group][0], self::$each[self::$group][1]);
		}
		return true;
	}

	/**
	 * Set each key over all defined groups to the value
	 * @param string $key
	 * @param mixed $val
	 */
	public static function seteach($key, $val=false)
	{
		self::$each[self::$group] = array($key, $val);
	}

	/**
	 * Load a struct layout from file and create an array from this file.
	 * Returns true when operation succeeded
	 * @param file $file
	 * @param array $array
	 * @return boolean
	 */
	public static function preset_from_file($file, &$array=false)
	{
		if( !file_exists($file) ) return false;
		if( $array === false )
		{
			$array = & self::$store;
		}


		$stream = file_get_contents($file);
		$rx = '#//.*?(\r\n|\n|\Z)+#';
		$stream = preg_replace($rx, "\$1", $stream);
		$rx = '#/\*.*?\*/#is';
		$stream = preg_replace($rx, "", $stream);
		$rx = '/; *(?:\r\n *|\n *|\Z)+/';
		$lines = preg_split($rx, $stream);
		if( $lines )
		{
			foreach( $lines as $line )
			{
				$line = $line.";";
				$line = ltrim($line);
				if( trim($line) )
				{
					$S = " \\t\\r\\n"; //Whitespaces
					$S = "\\s"; //Whitespaces
					$C = "//|SET|GROUP|EACH"; //Command
					$K = "[a-zA-Z0-9_#+~.]"; //Key
					$L = "(?:->|=>)"; //Leveldelimiter
					$A = "(?::|=)"; //Assignmentdelimiter
					$E = ";"; //Endline command
					//[ \t\r\n]*(//|SET|GROUP|EACH)?[ \t\r\n]*((?:(?:[a-zA-Z0-9_#+~.]|(?:=>|->))(?:(?:->|=>)|(?::|=))*?)*)(?::|=)?((?:\\;|[^;])*?);
					$rx = "[".$S."]*(".$C.")?[".$S."]*((?:(?:".$K."|(?:=>|->|-))(?:".$L."|".$A.")*?)*)".$A."?((?:\\\\".$E."|[^".$E."])*?);";
					if( substr(ltrim($line), 0, 2) == "//" || substr(ltrim($line), 0, 1) == "#" )
					{
						//This line is a comment and gets no attention
					}
					elseif( preg_match("!".$rx."!", ltrim($line), $match) )
					{
						//print_r($match);
						switch ($match[1])
						{
							case "GROUP":
								self::group($match[2]);
								break;
							case "EACH":
								self::seteach($match[2], $match[3]);
								break;
							case "SET":
								self::set($match[2], $match[3], $array);
								break;
							case "":
								//$split = preg_split("/".$A."/", $match[2], 2);
								$key = trim($match[2]);
								$val = trim($match[3]);
								self::def($key, $val);
								break;
						}
					}
					else
					{
						throw new Exception("Something went wrong!");
					}
				}
			}
		}
		return true;
	}

	/**
	 * Set a session
	 * @param string $id
	 */
	public static function session($id)
	{
		self::$sesh = md5($id);
	}

	/**
	 * Define the creator library for struct output. This must be an valid instanciated object in a special named form. Please refer to the manual for this method.
	 * @param object $obj
	 */
	public static function creator(&$obj)
	{
		self::$creator[] = $obj;
	}

	/**
	 * Append a mixed value to a key
	 * @param array $array
	 * @param string $key
	 * @param mixed $data
	 */
	private static function set_array(&$array, $key, $data)
	{
		$rx = "/^((?:[^=>]|[^->])*?)(?:=>|->|\Z)(.*)/";
		preg_match($rx, $key, $match);

		self::$accesskey[$key] = md5($key);
		self::$keyaccess[md5($key)] = $key;

		if( !$match[2] )
		{
			if( !trim($match[1]) )
			{
				$array[] = $data;
			}
			else
			{
				$array[$match[1]] = $data;
			}
			return true;
		}
		if( !trim($match[1]) ) self::set_array($array[], $match[2], $data);
		else self::set_array($array[$match[1]], $match[2], $data);
	}

	/**
	 * This method is an alias for get_meta
	 * @param string $key
	 * @return array
	 */
	public static function get_def($key)
	{
		return self::get_meta($key);
	}

	/**
	 * Get meta data to associated key
	 * @param string $key
	 * @return mixed
	 */
	public static function get_meta($key=null)
	{
		return (isset($key) ? self::get($key, self::$meta) : self::$meta);
	}

	public static function getmeta($key=null)
	{
		return self::get_meta($key);
	}

	/**
	 * Get a key from the struct. If array is not given, it will try to get the desired data from current active array. If key is not defined, it returns the whole array.
	 * @param string $key
	 * @param array $array
	 * @return mixed
	 */
	public static function get($key=null, $array=false)
	{
		if( $array === false )
		{
			$array = & self::$store;
		}

		if( !isset($key) ) return $array;

		//print_r(self::$keyaccess);

		if( isset(self::$keyaccess[$key]) ) $key = self::$keyaccess[$key];

		preg_match("/^((?:[^=>]|[^->])*?)(?:=>|->|\Z)(.*)/", $key, $match);
		$nextkey = self::no_case_decision($array, $match[1]);

		//print_r($nextkey);

		if( trim($match[2]) )
		{
			if( isset($array[$nextkey]) )
			{
				return self::get($match[2], $array[$nextkey]);
			}
		}
		else
		{
			return $array[$nextkey];
		}
	}

	/**
	 * Helper, tries to determine the given key regardless of the given case
	 * @param array $stack
	 * @param string $needle
	 * @return mixed or raises an error.
	 */
	private static function no_case_decision($stack, $needle)
	{
		if( isset($stack[$needle]) ) return $needle;
		if( !is_array($stack) ) return $needle;
		$list = array_keys($stack);
		if( $list )
		{
			foreach( $list as $keyname )
			{
				if( strtolower($keyname) == strtolower($needle) )
				{
					$suggest[] = $keyname;
				}
			}
			if( count($suggest) >= 2 )
			{
				trigger_error('You tried to access "'.$needle.'" but there are only "'.implode('", "', $suggest).'" [CASE DECISION PROBLEM]', E_USER_WARNING);
				return self::suggestion_decision_maker($needle, $suggest);
				return $needle;
			}
			elseif( count($suggest) == 1 )
			{
				return current($suggest);
			}
			else
			{
				trigger_error('No value to access by "'.$needle.'"', E_USER_WARNING);
				return $needle;
			}
		}
	}

	private static function suggestion_decision_maker($needle, $suggestionlist)
	{
		foreach( $suggestionlist as $suggested )
		{
			$checklist[$suggested] = similar_text($needle, $suggested);
		}
		arsort($checklist);
		reset($checklist);
		return key($checklist);
	}

	/**
	 * Includes style stream from css file into html
	 * @param templatename $skin
	 */
	private static function _css($skin=false)
	{
		$defaultskin = dirname(__FILE__)."/".__CLASS__."/"."default"."/"."style.css";
		$userskin = dirname(__FILE__)."/".__CLASS__."/".$skin."/"."style.css";
		if( !$skin && file_exists($defaultskin) )
		{
			$style = file_get_contents($defaultskin);
		}
		elseif( $skin && file_exists($userskin) )
		{
			$style = file_get_contents($userskin);
		}
		if( $style )
		{
			struct::h("<style>");
			echo str_replace("%path%", "classes/".__CLASS__, $style);
			struct::h("</style>");
		}
	}

	/**
	 * Includes JS-Functions in html
	 */
	private static function _js()
	{
		$js = <<<EOF
function col(){}
col.setclean = function(){return '#FFFFFF';}
col.setdirty = function(){return '#FFE2C6';}
function struct(){}
struct.reclass=function(el,className)
{if(el.className.indexOf(className)<0)
{el.className=el.className.trim();el.className+=(' '+className);}}
struct.unclass=function(el,className)
{if(el.className.indexOf(className)>-1)
{el.className=el.className.replace(className,'');}}
struct.hasclass=function(el,className)
{if(el.className.indexOf(className)>-1)
{if(el.className.search(className)>-1)
{return true;}}
return false;}
struct.toggle=function(el){var ul=el.parentNode.getElementsByTagName('ul');for(var i=0;i<ul.length;i++)
{if(ul[i].parentNode.parentNode==el.parentNode)
{if(struct.hasclass(el,'is_expanded'))
{ul[i].parentNode.style.display='none';struct.unclass(el,'is_expanded');struct.createCookie(el.id,"1");}
else
{ul[i].parentNode.style.display='block';struct.reclass(el,'is_expanded');struct.unclass(ul[i].parentNode,'init_hidden');struct.eraseCookie(el.id);}}}}
struct.over=function(el)
{struct.reclass(el,'struct_hover');}
struct.out=function(el)
{struct.unclass(el,'struct_hover');}
struct.createCookie=function(name,value,days){if(days){var date=new Date();date.setTime(date.getTime()+(days*24*60*60*1000));var expires="; expires="+date.toGMTString();}
else var expires="";document.cookie=name+"="+value+expires+"; path=/";}
struct.readCookie=function(name){var nameEQ=name+"=";var ca=document.cookie.split(';');for(var i=0;i<ca.length;i++){var c=ca[i];while(c.charAt(0)==' ')c=c.substring(1,c.length);if(c.indexOf(nameEQ)==0)return c.substring(nameEQ.length,c.length);}
return null;}
struct.eraseCookie=function(name){struct.createCookie(name,"",-1);}
struct.ajax=function(){try{return new XMLHttpRequest();}catch(e){}
try{return new ActiveXObject("Msxml2.XMLHTTP");}catch(e){}
alert("XMLHttpRequest not supported");return null;}
struct.send=function(target,requeststr){var request=struct.ajax();request.open("GET",target+"?"+requeststr,true);request.onreadystatechange=function(){if(request.readyState!=4){return;}
var serverResponse=request.responseText;if(serverResponse!=""){alert(serverResponse);}}
request.send(null);}
struct.increment=function(button,steps,target,initialDelay,multiplier){var holdTimer,changeValue,timerIsRunning=false,delay=initialDelay;changeValue=function(){if(steps>0){target.value=parseInt(target.value)+parseInt(steps);target.onkeyup();target.click();}
else if(steps<0){target.value=parseInt(target.value)+parseInt(steps);target.onkeyup();target.click();}
holdTimer=setTimeout(changeValue,delay);if(delay>20)delay=delay*multiplier;if(!timerIsRunning){document.onmouseup=function(){clearTimeout(holdTimer);document.onmouseup=null;timerIsRunning=false;delay=initialDelay;}
timerIsRunning=true;}}
button.onmousedown=changeValue;}
struct.reset=function(target,val){target.value=val;target.onkeyup();target.click();}
struct.setclean=function(target){target.style.background=col.setclean();}
struct.setdirty=function(target){target.style.background=col.setdirty();}
EOF;
		struct::h("<script>");
		echo $js;
		struct::h("</script>");
	}

	/**
	 * This method creats the whole html code for an given struct or key
	 * @param array $array
	 * @param string $key
	 * @param boolean $return
	 * @return string
	 */
	public static function draw($array=false, $key=false, $return=false)
	{
		//			echo "ISARRAY:".is_array($array).": ";
		//			echo "ARRAY:".$array.": ";
		//			echo "KEY:".$key.": ";
		//			echo "RETURN:".$return.": ";

		if( !is_array($array) && $array === false && $key === false && $return === false )
		{
			$array = self::$store;
		}
		elseif( !is_array($array) && $array !== false && $key === false && $return === false )
		{
			$return = $key;
			$key = $array;
			$array = self::$store;
		}
		elseif( !is_array($array) && $array !== false && $key !== false && $return === false )
		{
			$return = $key;
			$key = $array;
			$array = self::$store;
		}

		//			echo "ISARRAY:".is_array($array).": ";
		//			echo "ARRAY:".$array.": ";
		//			echo "KEY:".$key.": ";
		//			echo "RETURN:".$return.": ";
		//			exit;

		$buildin = new buildincontrols;
		self::creator($buildin);
		if( $return ) ob_start();
		self::_css("default");
		self::_js();
		struct::h('<div class="struct_root">');
		if( $key )
		{
			$array = self::get($key, $array);
			self::htmlarray($array, $key);
		}
		else
		{
			self::htmlarray($array);
		}
		struct::h('</div>');
		if( $return ) return ob_get_clean();
	}

	/**
	 * Creates a html structure for an array
	 * @param mixed $node
	 * @param string $currentkey
	 * @param boolean $inithidden
	 */
	private static function htmlarray($node, $currentkey="", $inithidden=false)
	{
		struct::h('<div class="listelement '.($inithidden ? "init_hidden" : "").'">');
		struct::h('<ul class="struct">');

		if( $node ) foreach( $node as $key => $data )
			{
				if( is_array($data) )
				{
					$expand = ($_COOKIE[md5(self::$sesh.$currentkey."->".$key)] ? "" : "is_expanded");
					$inithidden = ($_COOKIE[md5(self::$sesh.$currentkey."->".$key)] ? true : false);
					struct::h('<li class="node">');
					struct::h('<div class="element can_expand '.$expand.'" id="'.md5(self::$sesh.$currentkey."->".$key).'" onclick="struct.toggle(this);" onmouseover="struct.over(this);" onmouseout="struct.out(this);">');
					struct::h('<a class="name">');
					struct::h($key);
					struct::h('</a>');
					struct::h('</div>');
					self::htmlarray($data, (!$currentkey ? $key : $currentkey."->".$key), $inithidden);
					struct::h("</li>");
				}
				else
				{
					struct::h('<li class="node">');
					struct::h('<div class="element" onmouseover="struct.over(this);" onmouseout="struct.out(this);">');
					struct::h(self::build_node(($currentkey ? $currentkey."->" : "").$key, $data, $key));
					struct::h('</div>');
					struct::h('</li>');
				}
			}
		struct::h("</ul>");
		struct::h('</div>');
		return;
	}

	/**
	 * Creates a node for the given key
	 * @param string $key
	 * @param mixed $data
	 * @param string $name
	 */
	private static function build_node($key, $data, $name=null)
	{
		$node = new node;
		$node->command("input");
		$node->setProperty("name", md5($key));
		$node->setProperty("readonly", "readonly");
		self::$order++;
		$node->setProperty("tabindex", self::$order);

		$set = self::get($key, self::$meta);
		if( !self::$creator )
		{
			die("Cant find an element builder");
		}

		foreach( self::$creator as $cid => $cobj )
		{
			if( method_exists($cobj, "construct") )
			{
				$cobj->name = $name;
				$cobj->key = md5($key);
				$cobj->default = $set["default"];
				$cobj->param = "NA in construct";
				$cobj->value = &$data;
				$cobj->obj = &$node;
				call_user_func(array($cobj, "construct"));
			}
		}

		if( $set )
		{
			foreach( $set as $fn => $fv )
			{
				$fc = false;
				foreach( self::$creator as $cid => $cobj )
				{
					if( method_exists($cobj, $fn) && !$fc )
					{
						$cobj->name = $name;
						$cobj->key = md5($key);
						$cobj->default = $set["default"];
						$cobj->param = $fv;
						$cobj->value = &$data;
						$cobj->obj = &$node;
						call_user_func(array($cobj, $fn));
						$fc = true;
					}
				}
			}
		}

		$node->setProperty("value", $data);
		struct::h($node->get());
	}

	/**
	 * Swiss army knife for merging to structs or array into one.
	 * @param array $reference
	 * @param array $array_to_be_set
	 * @param boolean $just_alter_existing_values
	 * @param boolean $truncate_non_referenced
	 * @return boolean
	 */
	public static function array_adapt($reference, &$array_to_be_set=false, $just_alter_existing_values=null, $truncate_non_referenced=null)
	{
		if( isset($reference) && !is_array($array_to_be_set) )
		{
			$array_to_be_set = & self::$store;
		}

		if( $reference ) foreach( $reference as $key => $value )
			{
				if( !$just_alter_existing_values && is_array($reference[$key]) )
				{
					self::array_adapt($reference[$key], $array_to_be_set[$key], $just_alter_existing_values, $truncate_non_referenced);
				}
				elseif( $just_alter_existing_values && is_array($reference[$key]) && is_array($array_to_be_set[$key]) )
				{
					self::array_adapt($reference[$key], $array_to_be_set[$key], $just_alter_existing_values, $truncate_non_referenced);
				}
				elseif( !$just_alter_existing_values && !isset($array_to_be_set[$key]) )
				{
					$array_to_be_set[$key] = $value;
				}
				elseif( $just_alter_existing_values && isset($array_to_be_set[$key]) )
				{
					$array_to_be_set[$key] = $value;
				}
			}
		if( $truncate_non_referenced && $array_to_be_set ) foreach( $array_to_be_set as $key => $value )
			{
				if( $truncate_non_referenced && !isset($reference[$key]) )
				{
					unset($array_to_be_set[$key]);
				}
			}
		return true;
	}

	/**
	 * Kind of output buffer for single line html commands
	 * @param string $html
	 */
	private static function h($html)
	{
		echo $html.(substr($html, -1, 1) == ">" ? "" : "");
	}

}

class node
{

	var $command;
	var $attribute = array();
	var $value = array();
	var $after = array();
	var $before = array();
	var $id;

	function __construct()
	{
		$this->id = uniqid();
		$this->addProperty("id", $this->id);
	}

	function command($name)
	{
		$this->command = $name;
	}

	function setProperty($attr, $val)
	{
		$this->addProperty($attr, $val);
	}

	function addProperty($attr, $val)
	{
		$this->attribute[strtolower($attr)] = str_replace('"', '\\"', $val);
	}

	function delProperty($attr)
	{
		$this->remProperty($attr);
	}

	function remProperty($attr)
	{
		unset($this->attribute[strtolower($attr)]);
	}

	function setValue($val)
	{
		$this->addValue($val);
	}

	function addValue($val)
	{
		$this->insert($val);
	}

	function insert($val)
	{
		array_push($this->value, $val);
	}

	function insertBefore($val)
	{
		array_unshift($this->value, $val);
	}

	function getId()
	{
		return $this->id;
	}

	function getProperty($attr)
	{
		return $this->attribute[strtolower($attr)];
	}

	function hasProperty($attr)
	{
		return isset($this->attribute[strtolower($attr)]);
	}

	function append($data)
	{
		array_push($this->after, $data);
	}

	function after($data)
	{
		array_unshift($this->after, $data);
	}

	function prepend($data)
	{
		array_push($this->before, $data);
	}

	function before($data)
	{
		array_unshift($this->before, $data);
	}

	function get()
	{
		$code = "";
		$code.= $this->collect($this->before);

		if( $this->attribute ) foreach( $this->attribute as $an => $av )
				$aset[] = $an.'="'.$av.'"';

		$code.= "<".$this->command." ".@implode(" ", $aset).">";

		$code.= $this->collect($this->value);

		$code.= "</".$this->command.">";

		$code.= $this->collect($this->after);

		return $code;
	}

	private function collect($vault)
	{
		if( $vault )
		{
			foreach( $vault as $data )
			{
				if( is_object($data) )
				{
					$code.= $data->get();
				}
				else
				{
					$code.= $data;
				}
			}
		}
		return $code;
	}

}

class buildincontrols
{

	var $obj;
	var $default;
	var $key;
	var $param;
	var $value;
	var $name;

	function construct()
	{
		$ref = new node;
		$ref->command("input");
		$ref->addProperty("type", "hidden");
		$ref->addProperty("name", "ref-".$this->key);
		$ref->addProperty("value", $this->value);
		$this->obj->before($ref);
		$this->obj->setProperty("onkeyup", "if(getElementById('".$ref->getId()."').value != getElementById('".$this->obj->getId()."').value){struct.setdirty(this);}else{struct.setclean(this);}");
		$this->obj->setProperty("onsubmit", "getElementById('".$ref->getId()."').value = this.value;");
		//			$label = new node;
		//			$label->command("span");
		//			$label->addProperty("class","key");
		//			$label->setValue($this->name.": ");
		//			$this->obj->before($label);
	}

	public function iseditable()
	{
		$this->obj->delProperty("readonly");
		$ok = new node;
		$ok->command("input");
		$ok->setProperty("type", "submit");
		$ok->setProperty("onclick", "struct.send('','ajax=1&key=".$this->key."&val=' + getElementById('".$this->obj->getId()."').value + '&PHPSESSID=".session_id()."'); struct.setclean(getElementById('".$this->obj->getId()."'));");
		$ok->setProperty("value", "OK");
		$this->obj->append($ok);
	}

	public function isnumeric()
	{
		if( $this->param != "" && $this->param !== true ) $this->value = sprintf($this->param, $this->value);

		$this->obj->setProperty("size", strlen($this->value) * 1.3);
		$add = new node;
		$add->command("input");
		$add->setProperty("type", "button");
		$add->setProperty("value", "+");
		$add->setProperty("onmouseover", 'struct.increment(this,1,getElementById(\''.$this->obj->getId().'\'),300,0.9);');
		$reset = new node;
		$reset->command("input");
		$reset->setProperty("type", "button");
		$reset->setProperty("value", "0");
		$reset->setProperty("title", "Resets the value to default: ".$this->default);
		$reset->setProperty("onclick", 'getElementById(\''.$this->obj->getId().'\').value = \''.$this->default.'\'; '."struct.setclean(getElementById('".$this->obj->getId()."'));");
		$sub = new node;
		$sub->command("input");
		$sub->setProperty("type", "button");
		$sub->setProperty("value", "-");
		$sub->setProperty("onmouseover", 'struct.increment(this,-1,getElementById(\''.$this->obj->getId().'\'),300,0.9);');
		$this->obj->after($sub);
		$this->obj->after($reset);
		$this->obj->after($add);
	}

	public function minval()
	{
		if( is_numeric($this->param) )
		{
			$chk = new node;
			$chk->command("script");
			$chk->setValue("document.getElementById('".$this->obj->getId()."').addEventListener('click',function(){if(this.value <= ".$this->param."){this.value = ".$this->param.";} },false);");
			$this->obj->after($chk);
		}
	}

	public function ispassword()
	{
		$this->obj->setProperty("type", "password");
	}

	public function isalwaysuppercase()
	{
		$this->value = strtoupper($this->value);
		$label = new node;
		$label->command("span");
		$label->addProperty("title", "Das ist ein Hinweistext!");
		$label->setValue("* is always uppercase");
		$this->obj->append($label);
	}

}

?>